// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer_plugin/protocol/protocol_generated.dart';

/// An object that manages the subscriptions for analysis results.
///
/// Clients may not extend, implement or mix-in this class.
class SubscriptionManager {
  /// The current set of subscriptions.
  Map<AnalysisService, List<String>>? _subscriptions;

  /// Initialize a newly created subscription manager to have no subscriptions.
  SubscriptionManager();

  /// Return `true` if the file with the given [filePath] has a subscription for
  /// the given [service].
  bool hasSubscriptionForFile(String filePath, AnalysisService service) {
    var subscriptions = _subscriptions;
    if (subscriptions == null) {
      return false;
    }
    var files = subscriptions[service];
    return files != null && files.contains(filePath);
  }

  /// Return a list of the services for which the file with the given [filePath]
  /// has been subscribed.
  List<AnalysisService> servicesForFile(String filePath) {
    var services = <AnalysisService>[];
    var subscriptions = _subscriptions;
    if (subscriptions != null) {
      subscriptions.forEach((AnalysisService service, List<String> files) {
        if (files.contains(filePath)) {
          services.add(service);
        }
      });
    }
    return services;
  }

  /// Set the current set of subscriptions to those described by the given map
  /// of [subscriptions]. Return a map representing the subset of the
  /// subscriptions that are new. These are the subscriptions for which a
  /// notification should be sent. The returned map is keyed by the path of each
  /// file for which notifications should be send and has values representing
  /// the list of services that were added for that file.
  Map<String, List<AnalysisService>> setSubscriptions(
    Map<AnalysisService, List<String>> subscriptions,
  ) {
    var newSubscriptions = <String, List<AnalysisService>>{};
    var currentSubscriptions = _subscriptions;
    if (currentSubscriptions == null) {
      // This is the first time subscriptions have been set, so all of the
      // subscriptions are new.
      subscriptions.forEach((AnalysisService service, List<String> paths) {
        for (var path in paths) {
          newSubscriptions
              .putIfAbsent(path, () => <AnalysisService>[])
              .add(service);
        }
      });
    } else {
      // The subscriptions have been changed, to we need to compute the
      // difference.
      subscriptions.forEach((AnalysisService service, List<String> paths) {
        var oldPaths = currentSubscriptions[service];
        for (var path in paths) {
          if (oldPaths == null || !oldPaths.contains(path)) {
            newSubscriptions
                .putIfAbsent(path, () => <AnalysisService>[])
                .add(service);
          }
        }
      });
    }
    _subscriptions = subscriptions;
    return newSubscriptions;
  }
}
